package cn.blnp.net.flowable.event.end.error;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * <h3>结束事件-错误结束事件</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/7 17:04
 */
@Slf4j
@SpringBootTest
public class ErrorEventEnd {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;

    /**
     *  <p>
     *      流程流转到错误结束事件时，会结束当前的流程分支，并抛出错误。该错误可以被事件子流程中引用相同错误码的错误开始事件捕获，从而启动事件子流程。也可以被错误边界事件捕获。
     *  </p>
     *  <p>
     *      错误结束事件一般用在内嵌子流程或调用活动中，错误抛出后触发依附在边界上的错误边界事件。如果错误没有被任何错误开始事件或错误边界事件捕获，工作流引擎会抛出异常。
     *  </p>
     *  <pre>
     *      {@code
     *          <!-- 定义消息 -->
     *          <error id="theError" errorCode="theErrorCode"/>
     *          <process id="errorEndEventProcess">
     *          	<!-- 定义结束节点 -->
     *          	<endEvent id="errorEnd">
     *          		<!-- 包含errorEventDefinition子元素，代表错误结束事件 -->
     *          		<errorEventDefinition errorRef="theError" />
     *          	</endEvent>
     *          	<!-- 其他元素省略 -->
     *          </process>
     *      }
     *  </pre>
     *  <br/>
     *  <h2>演示案例</h2>
     *  <p>
     *      错误结束事件一般与错误边界事件搭配使用，通常在内嵌子流程和调用活动中使用，由错误结束事件抛出BPMN错误、由错误边界事件捕获该错误。如果找不到匹配的错误边界事件，则会抛出异常。
     *  </p>
     *  <p>
     *      <b>流程定义文件：</b>{@link flow/errorEventEnd.bpmn20.xml}<br/>
     *      <b>流程预览图：</b><br/>
     *      <img src="https://free4.yunpng.top/2025/05/07/681b27cc0e787.png" />
     *  </p>
     *  <p>
     *      该流程为电商采购流程。用户发起流程，完成下单操作之后进入付款子流程。如果付款成功，则付款子流程流转到空结束事件从而结束子流程，进入后续的“发货”节点；如果付款失败，则触发错误结束事件，
     *   抛出错误并结束子流程，附属在子流程节点上的错误边界事件捕获到错误信息，重新发起付款子流程。
     *  </p>
     *  <p>
     *      其中，错误结束事件endEvent3、错误边界事件boundaryEvent1引用同一个BPMN错误payError。在该示例中，判断是否付款成功用到了排他网关：如果流程变量payResult的值为true，则正常结束子流程；
     *   如果流程变量payResult的值为false，则触发错误结束事件。
     *  </p>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("错误结束事件演示案例")
                .addClasspathResource("flow/errorEventEnd.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        //启动流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("errorEndEventProcess");
        log.info("启动流程实例：{}", processInstance.getId());
        //查询当前任务并完成下单操作
        Task orderTask = taskService.createTaskQuery()
                .processInstanceId(processInstance.getId())
                .singleResult();
        log.info("当前任务：{}", orderTask.getName());
        log.info("当前任务ID：{}", orderTask.getId());
        log.info("当前任务审批人：{}", orderTask.getAssignee());
        taskService.complete(orderTask.getId());
        log.info("下单任务完成");
        Task payTask = taskService.createTaskQuery()
                .processInstanceId(processInstance.getId())
                .singleResult();
        log.info("当前任务：{}", payTask.getName());
        log.info("当前任务ID：{}", payTask.getId());
        log.info("当前任务审批人：{}", payTask.getAssignee());
        //设置payResult变量值
        Map<String, Object> varMap = MapUtil.builder(new HashMap<String, Object>())
                .put("payResult", false)
                .build();
        taskService.complete(payTask.getId(), varMap);
        log.info("付款任务完成");
        Task curTask = taskService.createTaskQuery()
                .processInstanceId(processInstance.getId())
                .singleResult();
        log.info("当前流程到达的用户任务名称为:{}",curTask.getName());

        /**
         *  演示结果：
         * 2025-05-08 13:49:56.195  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 部署流程定义：44dea8f1-2bd0-11f0-8e5a-50ebf6ec8f6d
         * 2025-05-08 13:49:56.257  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 启动流程实例：45296d95-2bd0-11f0-8e5a-50ebf6ec8f6d
         * 2025-05-08 13:49:56.270  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 当前任务：下单
         * 2025-05-08 13:49:56.270  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 当前任务ID：452ca1ea-2bd0-11f0-8e5a-50ebf6ec8f6d
         * 2025-05-08 13:49:56.270  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 当前任务审批人：null
         * 2025-05-08 13:49:56.320  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 下单任务完成
         * 2025-05-08 13:49:56.324  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 当前任务：付款
         * 2025-05-08 13:49:56.324  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 当前任务ID：453617d4-2bd0-11f0-8e5a-50ebf6ec8f6d
         * 2025-05-08 13:49:56.324  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 当前任务审批人：null
         * 2025-05-08 13:49:56.396  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 付款任务完成
         * 2025-05-08 13:49:56.399  INFO 97156 --- [           main] c.b.n.f.event.end.error.ErrorEventEnd    : 当前流程到达的用户任务名称为:付款
         **/
    }
}
